package org.cevinedit.editor.view.listeners;

import java.io.File;
import java.io.IOException;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;

import org.cevinedit.core.eugenia.EugeniaAnnotator;
import org.cevinedit.core.eugenia.EugeniaAnnotatorException;
import org.cevinedit.core.eugenia.EugeniaColor;
import org.cevinedit.core.tools.CevineditProjectTools;
import org.cevinedit.core.tools.EcoreTools;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.ui.part.FileEditorInput;

import cevinedit.CEViNEditRoot;
import cevinedit.CompartmentEReferenceCont;
import cevinedit.Diagram;
import cevinedit.LinkEClass;
import cevinedit.LinkEReferenceNonCont;
import cevinedit.NodeEClass;
import cevinedit.PersonalizedElement;
import cevinedit.presentation.CevineditEditor;

public class CevineditEditorGenerateEMFListener implements SelectionListener 
{
	protected CevineditEditor _editor = null;
	public CevineditEditorGenerateEMFListener(CevineditEditor editor)
	{
		_editor = editor;
	}
	
	public static CevineditEditorGenerateEMFListener i(CevineditEditor editor)
	{
		return new CevineditEditorGenerateEMFListener(editor);
	}
	
	public Resource basicEugeniaAnnotation(Resource r)
	{
		for(EObject eobject : r.getContents())
		{
			if (eobject instanceof EPackage)
			{
				EPackage epackage = (EPackage) eobject;				

				try {
					EugeniaAnnotator.annotateEObjectAsGMF(epackage);
				} 
				catch (EugeniaAnnotatorException e1) {
					// TODO Auto-generated catch block
					System.out.println(e1.getMessage());
				}
				
				for(EClassifier eclassifier : epackage.getEClassifiers())
				{
					if (eclassifier instanceof EClass)
					{
						EClass eclass = (EClass) eclassifier;
						
						try 
						{
							EugeniaAnnotator.annotateEObjectAsGMFNode(eclass);
						} 
						catch (EugeniaAnnotatorException e1) {
							// TODO Auto-generated catch block
							System.out.println(e1.getMessage());
						}
						
						for(EStructuralFeature esf : eclass.getEStructuralFeatures())
						{
							if (esf instanceof EAttribute)
							{
								try 
								{
									EugeniaAnnotator.annotateEObjectAsGMFLabel(esf);
								} 
								catch (EugeniaAnnotatorException e) 
								{
									// TODO Auto-generated catch block
									System.out.println(e.getMessage());
								}
							}
							if (esf instanceof EReference)
							{
								EReference eref = (EReference) esf;
								
								if (eref.isContainment())
								{								
									try 
									{
										EugeniaAnnotator.annotateEObjectAsGMFCompartment(eref);
									} 
									catch (EugeniaAnnotatorException e) 
									{
										// TODO Auto-generated catch block
										System.out.println(e.getMessage());
									}
								}
								else
								{
									try 
									{
										EugeniaAnnotator.annotateEObjectAsGMFLink(eref);
									} 
									catch (EugeniaAnnotatorException e) 
									{
										// TODO Auto-generated catch block
										System.out.println(e.getMessage());
									}									
								}
							}							

						}

					}
				}
			}
		}
		
		return r;
	}
	/*
	public PersonalizedElement getPersonalizedElement(CEViNEditRoot model, String name)
	{
			
		for (PersonalizedElement pe : model.getDiagram().getContainsElem())
		{
			if (pe.getName().equals(name))
			{
				return  pe;
			}
		}
		
		return null;
		
	}
	
	public PersonalizedElement getPersonalizedElement(CEViNEditRoot model, String name)
	{
			
		for (PersonalizedElement pe : model.getDiagram().getContainsElem())
		{
			if (pe.getName().equals(name))
			{
				return  pe;
			}
		}
		
		return null;
		
	}*/
	
	protected static Hashtable<String, PersonalizedElement> transformCevineditModel2HashTable(CEViNEditRoot model)
	{
		Hashtable<String, PersonalizedElement> elements = new Hashtable<>();
		
		for(PersonalizedElement pe : model.getDiagram().getContainsElem())
		{
			elements.put(pe.getName(), pe);
		}
		
		return elements;
	}
	
	public static List<Entry<String, String>> generateEntries(PersonalizedElement pe)
	{
		LinkedList<Entry<String,String>> lpe = new LinkedList<>();
		if (pe instanceof NodeEClass)
		{
			NodeEClass ne = (NodeEClass) pe;
			
			// Background Color
			int rgb[] = EugeniaColor.ColorToRGB((ne.getBackgroundColor().getLiteral()));
			
			lpe.add(EugeniaAnnotator.GmfNode_Color(rgb[0], rgb[1], rgb[2]));
			
			// Border Color
			rgb = EugeniaColor.ColorToRGB((ne.getBorderColor().getLiteral()));
			
			lpe.add(EugeniaAnnotator.GmfNode_BorderColor(rgb[0], rgb[1], rgb[2]));
					
			// Border Texture			
			try 
			{
				lpe.add(EugeniaAnnotator.GmfNode_BorderStyle(ne.getBorderTexture().getLiteral().toLowerCase()));
			} 
			catch (EugeniaAnnotatorException e) {
				// TODO Auto-generated catch block
				System.out.println(e.getMessage());
			}
			
			// Border Width
			lpe.add(EugeniaAnnotator.GmfNode_BorderWidth(ne.getBorderWidth()));
			
			// Brightness
			//lpe.add(EugeniaAnnotator.gmfn)
			
			// Figure
			try 
			{
				lpe.add(EugeniaAnnotator.GmfNode_Figure(ne.getFigure().getLiteral().toLowerCase()));
			} 
			catch (EugeniaAnnotatorException e) {
				// TODO Auto-generated catch block
				System.out.println(e.getMessage());
			}
			
			// Resizable
			lpe.add(EugeniaAnnotator.GMFNode_Resizable(ne.isResizable()));
			
			// Size
			if ((ne.getSize()!= null) &&(ne.getSize().indexOf(',') >= 0))
			{
				String x_y[] = ne.getSize().split(",");
				lpe.add(EugeniaAnnotator.GMFNode_Size(Integer.valueOf(x_y[0].trim()), Integer.valueOf(x_y[1].trim())));
			}
			
			// Label Placement
			try 
			{
				lpe.add(EugeniaAnnotator.GMFNode_LabelPlacement(ne.getLabelPlacement().getLiteral()));
			} 
			catch (EugeniaAnnotatorException e) {
				// TODO Auto-generated catch block
				System.out.println(e.getMessage());
			}
			
			// Label Font Style
			//lpe.add(EugeniaAnnotator.gmfnode_la)
			
			// Label
			lpe.add(EugeniaAnnotator.GMFNode_Label(ne.getLabel()));
		}
		
		if (pe instanceof LinkEClass)
		{
			LinkEClass le = (LinkEClass) pe;
			
			// Brightness
			
			// Color
			int rgb[] = EugeniaColor.ColorToRGB(le.getColor().getLiteral());
			lpe.add(EugeniaAnnotator.GMFLink_Color(rgb[0], rgb[1], rgb[2]));
			
			// Label Font Style
			
			// Source & Target Non Cont ERef

			lpe.add(EugeniaAnnotator.GMFLink_Source(le.getSource()));
			lpe.add(EugeniaAnnotator.GMFLink_Target(le.getTarget()));

			
			// Source Decoration 
			try 
			{
				lpe.add(EugeniaAnnotator.GMFLink_SourceDecoration(le.getSourceDecoration().getLiteral().toLowerCase()));
			} 
			catch (EugeniaAnnotatorException e) {
				// TODO Auto-generated catch block
				System.out.println(e.getMessage());
			}
			
			// Target Decoration
			try 
			{
				lpe.add(EugeniaAnnotator.GMFLink_TargetDecoration(le.getTargetDecoration().getLiteral().toLowerCase()));
			} 
			catch (EugeniaAnnotatorException e) {
				// TODO Auto-generated catch block
				System.out.println(e.getMessage());
			}
			// Texture
			try 
			{
				lpe.add(EugeniaAnnotator.GMFLink_Style(le.getTexture().getLiteral().toLowerCase()));
			} 
			catch (EugeniaAnnotatorException e) {
				// TODO Auto-generated catch block
				System.out.println(e.getMessage());
			}
			
			
			// Width
			lpe.add(EugeniaAnnotator.GMFLink_Width(Integer.valueOf(le.getWidth())));
			
			
		}
		
		if (pe instanceof LinkEReferenceNonCont)
		{
			LinkEReferenceNonCont le = (LinkEReferenceNonCont) pe;
			
			// Brightness
			
			// Color
			int rgb[] = EugeniaColor.ColorToRGB(le.getColor().getLiteral());
			lpe.add(EugeniaAnnotator.GMFLink_Color(rgb[0], rgb[1], rgb[2]));
			
			// Label Font Style
			
			// Source Decoration 
			try 
			{
				lpe.add(EugeniaAnnotator.GMFLink_SourceDecoration(le.getSourceDecoration().getLiteral().toLowerCase()));
			} 
			catch (EugeniaAnnotatorException e) {
				// TODO Auto-generated catch block
				System.out.println(e.getMessage());
			}
			
			// Target Decoration
			try 
			{
				lpe.add(EugeniaAnnotator.GMFLink_TargetDecoration(le.getTargetDecoration().getLiteral().toLowerCase()));
			} 
			catch (EugeniaAnnotatorException e) {
				// TODO Auto-generated catch block
				System.out.println(e.getMessage());
			}
			// Texture
			try 
			{
				lpe.add(EugeniaAnnotator.GMFLink_Style(le.getTexture().getLiteral().toLowerCase()));
			} 
			catch (EugeniaAnnotatorException e) {
				// TODO Auto-generated catch block
				System.out.println(e.getMessage());
			}
			
			
			// Width
			lpe.add(EugeniaAnnotator.GMFLink_Width(Integer.valueOf(le.getWidth())));
			
			
		}		
		
		if (pe instanceof CompartmentEReferenceCont)
		{
			CompartmentEReferenceCont compartment = (CompartmentEReferenceCont) pe;
			
			// Collapsible			
			lpe.add(EugeniaAnnotator.GMFCompartment_Collapsible(compartment.isCollapsible()));
			
			// Layout
			try 
			{
				lpe.add(EugeniaAnnotator.GMFCompartment_Layout(compartment.getLayout().getLiteral().toLowerCase()));
			} 
			catch (EugeniaAnnotatorException e) {
				// TODO Auto-generated catch block
				System.out.println(e.getMessage());
			}
		}
		
		return lpe;
	}
	
	public static void PersonalizeEClass(EClass eclass, PersonalizedElement element)
	{
		
		EAnnotation eannotation = null;
		
		eannotation = eclass.getEAnnotation("gmf.node");
		
		if (element instanceof LinkEClass)
		{			
			eannotation.setSource("gmf.link");
		}
		
		eannotation.getDetails().clear();
		
		
		for(Entry<String, String> entry : generateEntries(element))
		{
			eannotation.getDetails().put(entry.getKey(), entry.getValue());
		}
		
		eclass.getEAnnotations().clear();
		eclass.getEAnnotations().add(eannotation);
	}
	
	public static void PersonalizeEReference(EReference ereference, PersonalizedElement element)
	{
		
		EAnnotation eannotation = null;
		
		if (ereference.isContainment())		
			eannotation = ereference.getEAnnotation("gmf.compartment");
		else
			eannotation = ereference.getEAnnotation("gmf.link");
		
		
		eannotation.getDetails().clear();
		
		for(Entry<String, String> entry : generateEntries(element))
		{
			eannotation.getDetails().put(entry.getKey(), entry.getValue());
		}
	}
	
	public static void PersonalizeEClassAsDiagram(EClass eclass, Diagram diagram)
	{
		for (int i = 0; i < eclass.getEAnnotations().size(); i++) 
		{
			if (eclass.getEAnnotations().get(i).getSource().equals("gmf.node") ||
					eclass.getEAnnotations().get(i).getSource().equals("gmf.link"))
			{
				eclass.getEAnnotations().remove(i);
			}
		}

		for(EStructuralFeature es : eclass.getEStructuralFeatures())
		{
			es.getEAnnotations().clear();
		}
		
		try 
		{
			EugeniaAnnotator.annotateEObjectAsGMFDiagram(eclass);
			
			EAnnotation diagram_eannotation = eclass.getEAnnotation("gmf.diagram");
			Entry<String, String> entry_model_extension = EugeniaAnnotator.GmfDiagram_ModelExtension(diagram.getModelExtension().toLowerCase()+"_model");
			diagram_eannotation.getDetails().put(entry_model_extension.getKey(), entry_model_extension.getValue());
			Entry<String, String> entry_diagram_extension = EugeniaAnnotator.GmfDiagram_DiagramExtension(diagram.getModelExtension().toLowerCase()+"_diagram");
			diagram_eannotation.getDetails().put(entry_diagram_extension.getKey(), entry_diagram_extension.getValue());
		
		} catch (EugeniaAnnotatorException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
			
		
	}
	
	public Resource CevineditEugeniaAnnotation(Resource r, CEViNEditRoot model)
	{
		Hashtable<String, PersonalizedElement> personalizations = transformCevineditModel2HashTable(model);
		Diagram diagram = model.getDiagram();
		
		
		
		for(EObject eobject : r.getContents())
		{
			if (eobject instanceof EPackage)
			{
				EPackage epackage = (EPackage) eobject;				

				for(EClassifier eclassifier : epackage.getEClassifiers())
				{
					if (eclassifier instanceof EClass)
					{
						EClass eclass = (EClass) eclassifier;						
					
						if (eclass.getName().equals(diagram.getName()))
						{
							PersonalizeEClassAsDiagram(eclass, diagram);
						}
						else
						{
							if (personalizations.get(eclass.getName()) != null)
								PersonalizeEClass(eclass, personalizations.get(eclass.getName()));
						}
						
						for(EStructuralFeature esf : eclass.getEStructuralFeatures())
						{
							if (esf instanceof EAttribute)
							{

							}
							if (esf instanceof EReference)
							{
								EReference eref = (EReference) esf;
								
								if (personalizations.get(eref.getName()) != null)
									PersonalizeEReference(eref, personalizations.get(eref.getName()));
							}							

						}

					}
				}
			}
		}
		
		return r;
	}
	
	public Resource cleanEcoreResource(Resource r)
	{		
		
		for(EObject eobject : r.getContents())
		{
			if (eobject instanceof EPackage)
			{
				EPackage epackage = (EPackage) eobject;				

				for(EClassifier eclassifier : epackage.getEClassifiers())
				{
					if (eclassifier instanceof EClass)
					{
						EClass eclass = (EClass) eclassifier;
						
						for(EStructuralFeature esf : eclass.getEStructuralFeatures())
						{
							esf.getEAnnotations().clear();
						}
						
						eclass.getEAnnotations().clear();
					}
				}
				
				epackage.getEAnnotations().clear();
			}
		}
		
		
		
		return r;
	}
	
	
	@Override
	public void widgetSelected(SelectionEvent e) {
		// TODO Auto-generated method stub
		IPath path = ((FileEditorInput)_editor.getEditorInput()).getPath();
		
		IProject project = CevineditProjectTools.getActualWorkingProjectEditor(_editor);
		
		IFolder ecore_generated_folder = project.getFolder(CevineditProjectTools.DEFAULT_GENERATED_ECORE_FOLDER);
		
		IFile ecore_generated = ecore_generated_folder.getFile("generated.ecore");
		
		
		
		System.out.println("Generate EMF "+ecore_generated.getLocation().toOSString());
		
		Resource r = EcoreTools.loadEcore(ecore_generated.getLocation().toOSString());
		
		
		r = cleanEcoreResource(r);
		
		r = basicEugeniaAnnotation(r);
		
		r = CevineditEugeniaAnnotation(r, _editor.getCevineditModelRoot());
		
		EcoreTools.saveResource(r);
		
		
		String workspace = CevineditProjectTools.getWorkspaceRoot().getLocationURI().toString();
		
		
		String actualProjectName = CevineditProjectTools.getActualWorkingProjectEditor(_editor).getName();
		String ecoreGeneratedPath = workspace+ "/"+actualProjectName+"/.ecore_generated/generated.ecore";
		ecoreGeneratedPath = ecoreGeneratedPath.substring(6);
		
		Resource ecore = EcoreTools.loadEcore(ecoreGeneratedPath);
		
		EPackage root = (EPackage) ecore.getContents().get(0);
		
		
		
		String newEcoreGeneratedPath = workspace + "/" +actualProjectName+ "_gmf" + "/"+root.getName()+".ecore";
		newEcoreGeneratedPath = newEcoreGeneratedPath.substring(6);
		File fileEcoreGenerated = new File(ecoreGeneratedPath);
		File newFileEcoreGenerated = new File(newEcoreGeneratedPath);

		
		IProject newProject = CevineditProjectTools.getWorkspaceRoot().getProject(actualProjectName+ "_gmf/");
		try {
			if (!newProject.exists())
				newProject.create(null);
		} catch (CoreException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		try {
			Files.copy(fileEcoreGenerated.toPath(), newFileEcoreGenerated.toPath(), StandardCopyOption.REPLACE_EXISTING);
			//FileUtil.copy(fileEcoreGenerated, newFileEcoreGenerated); 
		} catch (IOException e2) {
			// TODO Auto-generated catch block
			System.out.println(e2.getMessage());
		}
		
		try {
			newProject.open(null);
		} catch (CoreException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}	
		
	}
	
	@Override
	public void widgetDefaultSelected(SelectionEvent e) {
		// TODO Auto-generated method stub
		
	}
}
